package cn.microvideo.videopf.camera.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.http.HttpUtil;
import cn.microvideo.videopf.camera.common.RestResult;
import cn.microvideo.videopf.camera.entity.CdnConfirmCameraReq;
import cn.microvideo.videopf.camera.entity.CdnConfirmCameraResp;
import cn.microvideo.videopf.camera.entity.CtyunOnlineStreamResponse;
import cn.microvideo.videopf.camera.entity.OnlineSteamResponse;
import cn.microvideo.videopf.camera.mapper.CameraMapper;
import cn.microvideo.videopf.camera.service.CameraService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.vs.model.v20181212.DescribeVsStreamsOnlineListRequest;
import com.aliyuncs.vs.model.v20181212.DescribeVsStreamsOnlineListResponse;
import com.google.common.collect.Lists;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.live.v20180801.LiveClient;
import com.tencentcloudapi.live.v20180801.models.DescribeLiveStreamOnlineListRequest;
import com.tencentcloudapi.live.v20180801.models.DescribeLiveStreamOnlineListResponse;
import com.tencentcloudapi.live.v20180801.models.StreamOnlineInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 摄像机服务实现
 *
 * @author 何欢-oreo
 * @version 1.0.0
 * @date 2020/8/20 17:32
 */
@Service
@Slf4j
public class CameraServiceImpl implements CameraService {
    /**
     * 第一页
     */
    private static final Integer ONE_PAGE_NUMBER = 1;

    /**
     * 每页100条
     */
    private static final Integer ONE_HUNDRED_PAGE_SIZE = 100;

    @Value("${second.environment.getStreamInfoBySmType.url}")
    private String secondGetStreamInfoBySmTypeUrl;

    @Value("${ctyun.domain}")
    private String ctyunDomain;

    @Value("${ctyun.token}")
    private String ctyunToken;

    @Value("${ctyun.api.url}")
    private String ctyunApiUrl;

    @Value("${tencent.accessSecretId}")
    private String tencentAccessSecretId;

    @Value("${tencent.accessSecretKey}")
    private String tencentAccessSecretKey;

    @Value("${tencent.region}")
    private String tencentRegion;

    @Value("${aliyun.regionId}")
    private String aliyunRegionId;

    @Value("${aliyun.accessKeyId}")
    private String aliyunAccessKeyId;

    @Value("${aliyun.accessKeySecret}")
    private String aliyunAccessKeySecret;

    /**
     * 摄像机持久层注入
     */
    @Resource
    private CameraMapper cameraMapper;

    /**
     * 日期格式化
     */
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");

    /**
     * 根据流媒体厂商类型获取所有列表
     *
     * @param smType 流媒体厂商类型
     * @return 流媒体列表
     */
    @Override
    public List<Map> getStreamInfoBySmType(String smType) {
        return cameraMapper.getStreamInfoBySmType(smType);
    }

    /**
     * 获取G1到G7的摄像机列表
     *
     * @return G1到G7的摄像机列表
     */
    @Override
    public List<Map> getCameraList() {
        return cameraMapper.getCameraList();
    }

    /**
     * 批量插入本环境G1到G7的摄像机初始数据到CDN确认表
     *
     * @return 插入记录数
     */
    @Override
    public int insertCdnConfirmCameraList() {
        return cameraMapper.insertCdnConfirmCameraList();
    }

    /**
     * 批量插入其他环境摄像机初始数据到CDN确认表
     *
     * @param list 摄像机列表
     * @return 插入记录数
     */
    @Override
    public int insertOtherCdnConfirmCameraList(List<Map> list) {
        return cameraMapper.insertOtherCdnConfirmCameraList(list);
    }

    /**
     * 更新摄像机在线时长
     *
     * @param gmtCreate     摄像机记录日期
     * @param cameraNumList 摄像机编码列表
     * @return 更新记录数
     */
    @Override
    public int updateCdnConfirmCameraOnlineDuration(String gmtCreate, List<String> cameraNumList) {
        return cameraMapper.updateCdnConfirmCameraOnlineDuration(gmtCreate, cameraNumList);
    }

    /**
     * 更新摄像机状态
     *
     * @param gmtCreate     摄像机记录日期
     * @param validDuration 在线有效时长
     * @return 更新记录数
     */
    @Override
    public int updateCdnConfirmCameraCameraStatus(String gmtCreate, Double validDuration) {
        return cameraMapper.updateCdnConfirmCameraCameraStatus(gmtCreate, validDuration);
    }

    /**
     * 删除过期的CDN确认的摄像机数据
     *
     * @param expireDate 过期日期
     * @return 删除记录数
     */
    @Override
    public int deleteExpireCdnConfirmCameraList(LocalDateTime expireDate) {
        return cameraMapper.deleteExpireCdnConfirmCameraList(expireDate);
    }

    /**
     * 获取天翼云在线流
     *
     * @return 天翼云在线流列表
     */
    @Override
    public RestResult getCtyunOnlineStream() {
        log.info("CameraServiceImpl.getCtyunOnlineStream start...");
        String url = String.format(ctyunApiUrl, ctyunToken, ctyunDomain);
        OnlineSteamResponse response;
        String strResult = HttpUtil.get(url);
        log.info("CameraServiceImpl.getCtyunOnlineStream strResult = {}", strResult);
        response = JSONObject.parseObject(strResult, OnlineSteamResponse.class);
        List<CtyunOnlineStreamResponse> responses = new ArrayList<>();
        for (OnlineSteamResponse.DataBean bean : response.getData()) {
            CtyunOnlineStreamResponse streamResponse = new CtyunOnlineStreamResponse();
            streamResponse.setStream(bean.getStream());
            responses.add(streamResponse);
        }
        log.info("CameraServiceImpl.getCtyunOnlineStream end");
        return new RestResult<>(responses);
    }

    /**
     * 获取腾讯云在线流
     *
     * @return 腾讯云在线流列表
     * @throws TencentCloudSDKException 腾讯云SDK异常
     */
    @Override
    public RestResult getTencentOnlineStream() throws TencentCloudSDKException {
        log.info("CameraServiceImpl.getTencentOnlineStream start...");

        //获取本环境流媒体信息
        List<Map> firstTencentStreamInfoList = cameraMapper.getStreamInfoBySmType("130");

        //获取第二环境流媒体信息
        String strResult = HttpUtil.get(String.format(secondGetStreamInfoBySmTypeUrl, "130"));
        JSONObject jsonObjectResult = JSON.parseObject(strResult);
        String strData = jsonObjectResult.getString("data");
        List<Map> secondTencentStreamInfoList = JSONArray.parseArray(strData, Map.class);

        List<Map> tencentStreamInfoList = Lists.newArrayList();
        if (CollectionUtil.isNotEmpty(firstTencentStreamInfoList)) {
            tencentStreamInfoList.addAll(firstTencentStreamInfoList);
        }
        if (CollectionUtil.isNotEmpty(secondTencentStreamInfoList)) {
            tencentStreamInfoList.addAll(secondTencentStreamInfoList);
        }
        List<Map<String, String>> returnListSum = new ArrayList<>();
        for (Map mapTemp : tencentStreamInfoList) {
            //推流域名
            String smPushIp = mapTemp.get("smPushIp") + "";
            //推流APP_NAME
            String pushAppName = mapTemp.get("pushAppName") + "";
            // 实例化一个认证对象，入参需要传入腾讯云账户secretId，secretKey
            Credential credential = new Credential(tencentAccessSecretId, tencentAccessSecretKey);
            LiveClient client = new LiveClient(credential, tencentRegion);
            // 实例化一个请求对象
            DescribeLiveStreamOnlineListRequest request = new DescribeLiveStreamOnlineListRequest();
            request.setDomainName(smPushIp);
            request.setAppName(pushAppName);
            //这里设置起始页为第一页，每页大小100条
            request.setPageNum(ONE_PAGE_NUMBER);
            request.setPageSize(ONE_HUNDRED_PAGE_SIZE);
            //返回列表
            List<Map<String, String>> returnList = new ArrayList<>();
            //返回集合
            Map<String, String> returnMap;
            // 通过client对象调用想要访问的接口，需要传入请求对象
            DescribeLiveStreamOnlineListResponse response;
            try {
                response = client.DescribeLiveStreamOnlineList(request);
            } catch (Exception e) {
                continue;
            }
            DescribeLiveStreamOnlineListResponse perResponse;
            for (int i = 1; i < response.getTotalPage() + 1; i++) {
                request.setPageNum(i);
                perResponse = client.DescribeLiveStreamOnlineList(request);
                for (StreamOnlineInfo streamOnlineInfo : perResponse.getOnlineInfo()) {
                    returnMap = new HashMap<>();
                    returnMap.put("streamName", streamOnlineInfo.getStreamName());
                    returnList.add(returnMap);
                }
            }
            returnListSum.addAll(returnList);
        }
        log.info("CameraServiceImpl.getTencentOnlineStream end");
        return new RestResult<>(returnListSum);
    }

    /**
     * 获取阿里云在线流
     *
     * @return 阿里云在线流列表
     * @throws ClientException 阿里云SDK异常
     */
    @Override
    public RestResult getAliyunOnlineStream() throws ClientException {
        log.info("CameraServiceImpl.getAliyunOnlineStream start...");
        IAcsClient acsClient = getClient(aliyunRegionId, aliyunAccessKeyId, aliyunAccessKeySecret);
        //获取本环境流媒体信息
        List<Map> firstAliyunLiveStreamInfoList = cameraMapper.getStreamInfoBySmType("110");
        List<Map> firstAliyunMonitorStreamInfoList = cameraMapper.getStreamInfoBySmType("111");

        //获取其他环境流媒体信息
        String aliyunLiveStrResult = HttpUtil.get(String.format(secondGetStreamInfoBySmTypeUrl, "110"));
        JSONObject aliyunLiveJsonObjectResult = JSON.parseObject(aliyunLiveStrResult);
        String aliyunLiveStrData = aliyunLiveJsonObjectResult.getString("data");
        List<Map> secondAliyunLiveStreamInfoList = JSONArray.parseArray(aliyunLiveStrData, Map.class);

        //获取第二环境流媒体信息
        String aliyunMonitorStrResult = HttpUtil.get(String.format(secondGetStreamInfoBySmTypeUrl, "111"));
        JSONObject aliyunMonitorJsonObjectResult = JSON.parseObject(aliyunMonitorStrResult);
        String aliyunMonitorStrData = aliyunMonitorJsonObjectResult.getString("data");
        List<Map> secondAliyunMonitorStreamInfoList = JSONArray.parseArray(aliyunMonitorStrData, Map.class);

        List<Map> aliyunStreamInfoList = Lists.newArrayList();
        if (CollectionUtil.isNotEmpty(firstAliyunLiveStreamInfoList)) {
            aliyunStreamInfoList.addAll(firstAliyunLiveStreamInfoList);
        }
        if (CollectionUtil.isNotEmpty(firstAliyunMonitorStreamInfoList)) {
            aliyunStreamInfoList.addAll(firstAliyunMonitorStreamInfoList);
        }
        if (CollectionUtil.isNotEmpty(secondAliyunLiveStreamInfoList)) {
            aliyunStreamInfoList.addAll(secondAliyunLiveStreamInfoList);
        }
        if (CollectionUtil.isNotEmpty(secondAliyunMonitorStreamInfoList)) {
            aliyunStreamInfoList.addAll(secondAliyunMonitorStreamInfoList);
        }
        List<String> camerasSum = Lists.newArrayList();
        for (Map streamResultMap : aliyunStreamInfoList) {
            DescribeVsStreamsOnlineListRequest request = new DescribeVsStreamsOnlineListRequest();
            String domainName = streamResultMap.get("smPushIp") + "";
            request.setDomainName(domainName);
            String appName = streamResultMap.get("pushAppName") + "";
            if (StringUtils.isBlank(appName)) {
                appName = "live";
            }
            request.setAppName(appName);
            request.setPageSize(500);
            DescribeVsStreamsOnlineListResponse response;
            try {
                response = acsClient.getAcsResponse(request);
            } catch (Exception e) {
                continue;
            }
            if (null != response && CollectionUtils.isNotEmpty(response.getOnlineInfo())) {
                //返回列表
                List<String> returnList = Lists.newArrayList();
                for (DescribeVsStreamsOnlineListResponse.LiveStreamOnlineInfo liveStreamOnlineInfo : response.getOnlineInfo()) {
                    returnList.add(liveStreamOnlineInfo.getStreamName());
                }
                camerasSum.addAll(returnList);
            }
        }
        log.info("CameraServiceImpl.getAliyunOnlineStream end");
        return new RestResult<>(camerasSum);
    }

    private IAcsClient getClient(String regionId, String acKeyId, String acKeySec) {
        DefaultProfile profile = DefaultProfile.getProfile(
                regionId, acKeyId, acKeySec);
        IAcsClient client = new DefaultAcsClient(profile);
        return client;
    }

    /**
     * 导出CDN确认的摄像机列表
     *
     * @param cdnConfirmCameraReq 请求参数
     * @param response            响应
     * @return 响应
     * @throws IOException IO异常
     */
    @Override
    public RestResult exportCdnConfirmCameraList(CdnConfirmCameraReq cdnConfirmCameraReq, HttpServletResponse response)
            throws IOException {
        log.info("CameraServiceImpl.exportCdnConfirmCameraList start with cdnConfirmCameraReq = {}", cdnConfirmCameraReq);
        if (null == cdnConfirmCameraReq.getGmtCreate()) {
            cdnConfirmCameraReq.setGmtCreate(LocalDate.now().minusDays(1).toString());
        }

        if (null == cdnConfirmCameraReq.getCameraStatus()) {
            cdnConfirmCameraReq.setCameraStatus(1);
        }
        List<CdnConfirmCameraResp> cdnConfirmCameraRespList = cameraMapper.getCdnConfirmCameraList(cdnConfirmCameraReq);
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode(cdnConfirmCameraReq.getGmtCreate() + (cdnConfirmCameraReq.getCameraStatus().equals(1) ? "在线摄像机列表" : "不在线摄像机列表"), "UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
        response.setHeader("Content-Type", "application/octet-stream");
        EasyExcel.write(response.getOutputStream(), CdnConfirmCameraResp.class).sheet().doWrite(cdnConfirmCameraRespList);
        log.info("CameraServiceImpl.exportCdnConfirmCameraList export {} items", cdnConfirmCameraRespList.size());
        return new RestResult<>("导出数据" + cdnConfirmCameraRespList.size() + "条");
    }
}
